/*
 * FreeRTOS+TCP <DEVELOPMENT BRANCH>
 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://aws.amazon.com/freertos
 * http://www.FreeRTOS.org
 */

/*
 * tcp_dump_packets.c
 * Used in the PC/Win project to dump Ethernet packets, along with some description.
 * See tools/tcp_dump_packets.md for further description.
 */

/* Standard includes. */
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <ctype.h>

/* FreeRTOS includes. */
#include <FreeRTOS.h>
#include "task.h"

/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_Stream_Buffer.h"
#include "FreeRTOS_IP_Private.h"

#if ( ipconfigUSE_DUMP_PACKETS != 0 )

    #include "tcp_dump_packets.h"

/* The priority of the windows thread. */
    #define dumpPROCESS_THREAD_PRIORITY    THREAD_PRIORITY_NORMAL

/* There is a stream buffer between the FreeRTOS tasks sending network packets,
 * and the Windows thread that writes these packets to disk. The macro 'dumpITEM_COUNT'
 * determines the number of full-size packets that can be stored in this stream buffer. */
    #ifndef dumpITEM_COUNT
        #define dumpITEM_COUNT    32
    #endif

/* Packets are written in hex notation, no more than 16 bytes on a row. */
    #ifndef dumpBYTES_PER_ROW
        #define dumpBYTES_PER_ROW    16
    #endif

/* The TCP port number reserved for a DNS server. */
    #define dnsDNS_PORT        0x0035u

/* Some const values describing the 'flags' in a TCP packet. */
    #define tcpTCP_FLAG_FIN    0x0001u          /* No more data from sender */
    #define tcpTCP_FLAG_SYN    0x0002u          /* Synchronize sequence numbers */
    #define tcpTCP_FLAG_RST    0x0004u          /* Reset the connection */
    #define tcpTCP_FLAG_PSH    0x0008u          /* Push function: please push buffered data to the recv application */
    #define tcpTCP_FLAG_ACK    0x0010u          /* Acknowledgment field is significant */

/* A macro to add a type, both as a numeric value, as well as a string. */
    #define ADD_TYPE( FLAGS ) \
    vAddType( flag_ ## FLAGS, # FLAGS )

/*-----------------------------------------------------------*/

    static char pcTypeString[ 255 ];
    static uint32_t ulTypeMask;

/* The name of the C source file to be written. */
    static char pcCodeFileName[ MAX_PATH ];

/* The name of the header file to be written. */
    static char pcHeaderFileName[ MAX_PATH ];

/* A stream buffer between the FreeRTOS tasks and the Windows thread. */
    static StreamBuffer_t * xPacketBuffer;

/* A process handle of the Windows thread. */
    static HANDLE pvProcessHandle;

    static UBaseType_t uxNextPacketNumber;
    static BaseType_t xFirstPacket = 1;

/* Boolean 'xDumpingReady' becomes true once all desired packet have been collected.
 * Further packets will be dropped (ignored). */
    static volatile BaseType_t xDumpingReady = pdFALSE;

    static DumpEntries_t * pxCurrentEntries;

    static uint16_t usSourcePort;
    static uint16_t usDestinationPort;

    typedef struct xBufferheader
    {
        size_t uxLength;
        UBaseType_t bIncoming : 1;
    } Bufferheader_t;

    static DumpEntries_t xExampleEntries =
    {
        .uxEntryCount = 4,                  /* No more than 'dumpMAX_DUMP_ENTRIES' elements. */
        .xEntries     =
        {
            { .ulMask = flag_IN | flag_UDP, .uxMax = 2u },
            { .ulMask = flag_IN | flag_ARP, .uxMax = 2u },
            { .ulMask = flag_IN | flag_TCP, .uxMax = 5u },
            { .ulMask = flag_IN | flag_SYN, .uxMax = 1u },
        }
    };

    const char pcHeaderCode[] =
        "/*\n"
        " * This file was created automatically by 'dump_packets.c'\n"
        " */\n"
        "\n"
        "/* Standard includes. */\n"
        "#include <stdio.h>\n"
        "#include <stdint.h>\n"
        "#include <stdarg.h>\n"
        "#include <io.h>\n"
        "#include <ctype.h>\n"
        "\n"
        "/* FreeRTOS includes. */\n"
        "#include <FreeRTOS.h>\n"
        "#include <task.h>\n\n"
        "#include \"%s\"\n\n";

    const char pcHeaderHeader[] =
        "/*\n"
        " * This file was created automatically by 'dump_packets.c'\n"
        " */\n"
        "\n"
        "#ifndef PACKET_LIST_H\n\n"
        "#define PACKET_LIST_H\n\n"
        "typedef struct xDumpPacket\n"
        "{\n"
        "	const uint8_t *pucData;\n"
        "	size_t uxLength;\n"
        "	uint32_t ulType;\n"
        "	uint16_t usSource;\n"
        "	uint16_t usDestination;\n"
        "} DumpPacket_t;\n\n";

/*-----------------------------------------------------------*/

/* The Windows thread that actually writes the network packets to a C source and header file. */
    static DWORD WINAPI prvWritePackets( LPVOID lpParameter );

    static void vAddProtocolTags( uint8_t * pucEthernetBuffer,
                                  BaseType_t xIPType );
    static void vDetermineMessageType( uint8_t * pucBuffer,
                                       BaseType_t xIncoming );
    static void vActualDump( uint8_t * pucBuffer,
                             size_t uxLength,
                             BaseType_t xIncoming );
    static void vAddType( uint32_t ulFlags,
                          const char * pcFlagName );
    static void vWriteHeaderFile( void );

/*-----------------------------------------------------------*/

    void dump_packet_init( const char * pcFileName,
                           DumpEntries_t * pxEntries )
    {
        size_t uxIndex;

        snprintf( pcCodeFileName, sizeof pcCodeFileName, "%s.c", pcFileName );
        snprintf( pcHeaderFileName, sizeof pcHeaderFileName, "%s.h", pcFileName );

        if( pxEntries == NULL )
        {
            pxEntries = &( xExampleEntries );
        }

        configASSERT( pxEntries->uxEntryCount > 0 );
        configASSERT( pxEntries->uxEntryCount <= dumpMAX_DUMP_ENTRIES );

        for( uxIndex = 0; uxIndex < pxEntries->uxEntryCount; uxIndex++ )
        {
            pxEntries->xEntries[ uxIndex ].uxCount = 0;
        }

        pxCurrentEntries = pxEntries;

        if( xPacketBuffer == NULL )
        {
            size_t uxLength, uxSize;

            /* Enough space for e.g. 32 buffers and length words. */
            uxLength = dumpITEM_COUNT * ( sizeof( void * ) + ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER );
            uxSize = ( sizeof( *xPacketBuffer ) + uxLength ) - sizeof( xPacketBuffer->ucArray );
            xPacketBuffer = ( StreamBuffer_t * ) pvPortMalloc( uxSize );
            configASSERT( xPacketBuffer != NULL );
            vStreamBufferClear( xPacketBuffer );
            xPacketBuffer->LENGTH = uxLength;
        }

        if( pvProcessHandle == NULL )
        {
            pvProcessHandle = CreateThread( NULL, 0, prvWritePackets, NULL, CREATE_SUSPENDED, NULL );

            if( pvProcessHandle != NULL )
            {
                SetThreadPriority( pvProcessHandle, dumpPROCESS_THREAD_PRIORITY );
                SetThreadPriorityBoost( pvProcessHandle, TRUE );
                SetThreadAffinityMask( pvProcessHandle, 0x0E );
                ResumeThread( pvProcessHandle );
            }
        }
    }
/*-----------------------------------------------------------*/

    void dump_packet( const uint8_t * pucBuffer,
                      size_t uxLength,
                      BaseType_t xIncoming )
    {
        /* This function shall be called from a normal FreeRTOS task only. */
        if( xPacketBuffer != NULL )
        {
            if( xDumpingReady == pdFALSE )
            {
                size_t uxSpace = uxStreamBufferGetSpace( xPacketBuffer );
                size_t uxNeeded = uxLength + sizeof( size_t );

                if( uxNeeded < uxSpace )
                {
                    Bufferheader_t xheader;

                    xheader.uxLength = uxLength;
                    xheader.bIncoming = xIncoming;
                    uxStreamBufferAdd( xPacketBuffer, 0u, ( const uint8_t * ) &( xheader ), sizeof( xheader ) );
                    uxStreamBufferAdd( xPacketBuffer, 0u, pucBuffer, uxLength );
                }
                else
                {
                    /* Drop this packet. */
                }
            }
            else
            {
                /* The Windows thread 'prvWritePackets()' had received enough packets.
                 * The packet buffer may be freed. */
                vPortFree( xPacketBuffer );
                xPacketBuffer = NULL;
            }
        }
    }
/*-----------------------------------------------------------*/

    static DWORD WINAPI prvWritePackets( LPVOID lpParameter )
    {
        /* This is a Windows thread, not a FreeRTOS task. FreeRTOS API's may not be called. */
        for( ; ; )
        {
            Sleep( 100 );

            while( ( xPacketBuffer != NULL ) && ( xDumpingReady == pdFALSE ) )
            {
                Bufferheader_t xHeader;
                size_t uxBytes = uxStreamBufferGetSize( xPacketBuffer );

                if( uxBytes <= sizeof( xHeader ) )
                {
                    break;
                }

                /* Peek the number of bytes available. */
                uxStreamBufferGet( xPacketBuffer, 0u, ( uint8_t * ) &( xHeader ), sizeof( xHeader ), pdTRUE );

                if( uxBytes >= sizeof( xHeader ) + xHeader.uxLength )
                {
                }

                {
                    size_t xBytesRead;
                    uint8_t pcBuffer[ ipconfigNETWORK_MTU ];
                    size_t xActualCount;

                    uxStreamBufferGet( xPacketBuffer, 0u, NULL, sizeof( xHeader ), pdFALSE );
                    xActualCount = uxStreamBufferGet( xPacketBuffer, 0u, pcBuffer, xHeader.uxLength, pdFALSE );
                    vActualDump( pcBuffer, xActualCount, xHeader.bIncoming );
                }
            }
        }
    }
/*-----------------------------------------------------------*/

    static int _fprintf( FILE * pxHandle,
                         const char * pcFormat,
                         ... )
    {
        char pcString[ 255 ];
        BaseType_t iCount;

        va_list args;

        va_start( args, pcFormat );
        iCount = vsnprintf( pcString, sizeof pcString, pcFormat, args );
        va_end( args );
        fwrite( pcString, 1u, iCount, pxHandle );

        return iCount;
    }
/*-----------------------------------------------------------*/

    static void vWriteHeaderFile( void )
    {
        FILE * outfile;

        outfile = fopen( pcHeaderFileName, "w" );

        if( outfile != NULL )
        {
            fwrite( pcHeaderHeader, 1u, sizeof( pcHeaderHeader ) - 1u, outfile );
            _fprintf( outfile, "#define dumpPACKET_COUNT %lu\n\n",
                      ( uxNextPacketNumber < 1u ) ? 1u : uxNextPacketNumber );
            _fprintf( outfile, "extern DumpPacket_t *xPacketList[ dumpPACKET_COUNT ];\n\n" );
            _fprintf( outfile, "#endif PACKET_LIST_H\n" );

            fclose( outfile );
        }
    }
/*-----------------------------------------------------------*/

    static void vAddType( uint32_t ulFlags,
                          const char * pcFlagName )
    {
        size_t uxLength = strlen( pcTypeString );
        char pcString[ 64 ];
        BaseType_t iCount;

        ulTypeMask |= ulFlags;

        if( uxLength == 0 )
        {
            snprintf( pcTypeString, sizeof pcTypeString, "%s", pcFlagName );
        }
        else
        {
            snprintf( pcTypeString + uxLength, sizeof pcTypeString - 1, " | %s", pcFlagName );
        }
    }
/*-----------------------------------------------------------*/

    static void vAddProtocolTags( uint8_t * pucEthernetBuffer,
                                  BaseType_t xIPType )
    {
        ProtocolHeaders_t * pxProtocolHeaders;

        #if ( ipconfigUSE_IPv6 != 0 )
            const IPHeader_IPv6_t * pxIPHeader_IPv6;
        #endif
        UBaseType_t uxHeaderLength;
        uint8_t ucProtocol;
        IPPacket_t * pxIPPacket;
        IPHeader_t * pxIPHeader;

        pxIPPacket = ( IPPacket_t * ) pucEthernetBuffer;
        pxIPHeader = &( pxIPPacket->xIPHeader );
        #if ( ipconfigUSE_IPv6 != 0 )
            pxIPHeader_IPv6 = ( const IPHeader_IPv6_t * ) &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER ] );

            if( pxIPPacket->xEthernetHeader.usFrameType == ipIPv6_FRAME_TYPE )
            {
                uxHeaderLength = ipSIZE_OF_IPv6_HEADER;
                ucProtocol = pxIPHeader_IPv6->ucNextHeader;
                pxProtocolHeaders = ( ProtocolHeaders_t * ) &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER ] );
            }
            else
        #endif
        {
            size_t uxLength = ( size_t ) pxIPHeader->ucVersionHeaderLength;

            /* Check if the IP headers are acceptable and if it has our destination.
             * The lowest four bits of 'ucVersionHeaderLength' indicate the IP-header
             * length in multiples of 4. */
            uxHeaderLength = ( size_t ) ( ( uxLength & 0x0Fu ) << 2 );
            ucProtocol = pxIPPacket->xIPHeader.ucProtocol;
            pxProtocolHeaders = ( ProtocolHeaders_t * ) &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxHeaderLength ] );
        }

        switch( ucProtocol )
        {
            case ipPROTOCOL_ICMP:
                ADD_TYPE( ICMP4 );
                break;

                #if ( ipconfigUSE_IPv6 != 0 )
                    case ipPROTOCOL_ICMP_IPv6:
                        ADD_TYPE( ICMP6 );
                        break;
                #endif

            case ipPROTOCOL_UDP:
                ADD_TYPE( UDP );
                usSourcePort = pxProtocolHeaders->xUDPHeader.usSourcePort;
                usDestinationPort = pxProtocolHeaders->xUDPHeader.usDestinationPort;

                if( usSourcePort == FreeRTOS_htons( dnsDNS_PORT ) )
                {
                    ADD_TYPE( DNS );
                    ADD_TYPE( REPLY );
                }
                else if( usDestinationPort == FreeRTOS_htons( dnsDNS_PORT ) )
                {
                    ADD_TYPE( DNS );
                    ADD_TYPE( REQUEST );
                }

                break;

                #if ipconfigUSE_TCP == 1
                    case ipPROTOCOL_TCP:
                        ADD_TYPE( TCP );
                        usSourcePort = pxProtocolHeaders->xTCPHeader.usSourcePort;
                        usDestinationPort = pxProtocolHeaders->xTCPHeader.usDestinationPort;

                        if( ( pxProtocolHeaders->xTCPHeader.ucTCPFlags & tcpTCP_FLAG_SYN ) != 0u )
                        {
                            ADD_TYPE( SYN );
                        }

                        if( ( pxProtocolHeaders->xTCPHeader.ucTCPFlags & tcpTCP_FLAG_FIN ) != 0u )
                        {
                            ADD_TYPE( FIN );
                        }

                        if( ( pxProtocolHeaders->xTCPHeader.ucTCPFlags & tcpTCP_FLAG_RST ) != 0u )
                        {
                            ADD_TYPE( RST );
                        }

                        if( ( pxProtocolHeaders->xTCPHeader.ucTCPFlags & tcpTCP_FLAG_ACK ) != 0u )
                        {
                            ADD_TYPE( ACK );
                        }
                        break;
                #endif /* if ipconfigUSE_TCP == 1 */
        }
    }
/*-----------------------------------------------------------*/

    static void vDetermineMessageType( uint8_t * pucBuffer,
                                       BaseType_t xIncoming )
    {
        EthernetHeader_t * pxEthernetHeader;

        if( xIncoming != 0 )
        {
            ADD_TYPE( IN );
        }
        else
        {
            ADD_TYPE( OUT );
        }

        pxEthernetHeader = ( EthernetHeader_t * ) pucBuffer;

        /* Interpret the received Ethernet packet. */
        switch( pxEthernetHeader->usFrameType )
        {
            case ipARP_FRAME_TYPE:
               {
                   ARPPacket_t * pxARPFrame;
                   ARPHeader_t * pxARPHeader;

                   /* The Ethernet frame contains an ARP packet. */
                   ADD_TYPE( FRAME_ARP );
                   pxARPFrame = ( ARPPacket_t * ) pucBuffer;
                   pxARPHeader = &( pxARPFrame->xARPHeader );
                   ADD_TYPE( ARP );

                   switch( pxARPHeader->usOperation )
                   {
                       case ipARP_REQUEST:
                           ADD_TYPE( REQUEST );
                           break;

                       case ipARP_REPLY:
                           ADD_TYPE( REPLY );
                           break;

                       default:
                           ADD_TYPE( UNKNOWN );
                           break;
                   }
               }
               break;

            case ipIPv4_FRAME_TYPE:
                ADD_TYPE( FRAME_4 );
                vAddProtocolTags( pucBuffer, 4 );
                break;

                #if ( ipconfigUSE_IPv6 != 0 )
                    case ipIPv6_FRAME_TYPE:
                        ADD_TYPE( FRAME_6 );
                        vAddProtocolTags( pucBuffer, 6 );
                        break;
                #endif
            default:
                /* No other packet types are handled.  Nothing to do. */
                ADD_TYPE( Unknown_FRAME );
                break;
        }
    }
/*-----------------------------------------------------------*/

    static void vActualDump( uint8_t * pucBuffer,
                             size_t uxLength,
                             BaseType_t xIncoming )
    {
        char pcString[ 513 ];
        size_t uxOffset;
        size_t uxIndex;
        size_t uxCompleteCount = 0;
        BaseType_t xUseIt = pdFALSE;

        usSourcePort = 0u;
        usDestinationPort = 0u;
        pcTypeString[ 0 ] = 0;
        ulTypeMask = 0uL;

        if( pxCurrentEntries == NULL )
        {
            return;
        }

        vDetermineMessageType( pucBuffer, xIncoming );

        for( uxIndex = 0; uxIndex < pxCurrentEntries->uxEntryCount; uxIndex++ )
        {
            if( pxCurrentEntries->xEntries[ uxIndex ].uxCount < pxCurrentEntries->xEntries[ uxIndex ].uxMax )
            {
                uint32_t ulMask = pxCurrentEntries->xEntries[ uxIndex ].ulMask;

                if( ( ulMask & ulTypeMask ) == ulMask )
                {
                    pxCurrentEntries->xEntries[ uxIndex ].uxCount++;
                    xUseIt = pdTRUE;
                }
            }
            else
            {
                uxCompleteCount++;
            }
        }

        FreeRTOS_printf( ( "prvWritePackets: done %d/%d : (%d,%d) (%d,%d) (%d,%d) (%d,%d)\n",
                           uxCompleteCount,
                           pxCurrentEntries->uxEntryCount,
                           pxCurrentEntries->xEntries[ 0 ].uxCount, pxCurrentEntries->xEntries[ 0 ].uxMax,
                           pxCurrentEntries->xEntries[ 1 ].uxCount, pxCurrentEntries->xEntries[ 1 ].uxMax,
                           pxCurrentEntries->xEntries[ 2 ].uxCount, pxCurrentEntries->xEntries[ 2 ].uxMax,
                           pxCurrentEntries->xEntries[ 3 ].uxCount, pxCurrentEntries->xEntries[ 3 ].uxMax ) );

        if( uxCompleteCount >= pxCurrentEntries->uxEntryCount )
        {
            FreeRTOS_printf( ( "prvWritePackets: all %lu packets have been collected\n", pxCurrentEntries->uxEntryCount ) );

            if( pxCurrentEntries != NULL )
            {
                FILE * outfile = fopen( pcCodeFileName, ( xFirstPacket != 0 ) ? "w" : "a+" );

                if( outfile == NULL )
                {
                    FreeRTOS_printf( ( "Can not create '%s'\n", pcCodeFileName ) );
                }
                else
                {
                    /*
                     *  Create a list with pointers to each network packet.
                     *  DumpPacket_t *xPacketList[ dumpPACKET_COUNT ] =
                     *  {
                     *      &xPacket_0000,
                     *      &xPacket_0001,
                     *      &xPacket_0002,
                     *      &xPacket_0003,
                     *  }
                     */
                    _fprintf( outfile, "\nDumpPacket_t *xPacketList[ dumpPACKET_COUNT ] =\n{\n" );

                    for( uxIndex = 0; uxIndex < uxNextPacketNumber; uxIndex++ )
                    {
                        _fprintf( outfile, "\t&xPacket_%04lu,\n", uxIndex );
                    }

                    _fprintf( outfile, "};\n" );
                    fclose( outfile );
                    vWriteHeaderFile();
                }

                pxCurrentEntries = NULL;

                /* Tell the thread and the function dump_packet() that packet
                 * dumping is ready. */
                xDumpingReady = pdTRUE;
            }

            return;
        }

        if( xUseIt == pdFALSE )
        {
            return;
        }

        printf( "prvWritePackets: Read %lu bytes, type %s\n", uxLength, pcTypeString );

        FILE * outfile = fopen( pcCodeFileName, ( xFirstPacket != 0 ) ? "w" : "a+" );

        if( outfile == NULL )
        {
            FreeRTOS_printf( ( "Can not create '%s'\n", pcCodeFileName ) );
            return;
        }

        if( xFirstPacket != 0 )
        {
            char * pcPtr;
            size_t xLength;

            vWriteHeaderFile( pcHeaderFileName );
            xLength = snprintf( pcString, sizeof pcString, pcHeaderCode, pcHeaderFileName );
            fwrite( pcString, 1u, xLength, outfile );
            xFirstPacket = pdFALSE;
        }

        _fprintf( outfile, "\n/* Packet_%04d */\n", uxNextPacketNumber );
        _fprintf( outfile, "uint8_t ucPacket_%04lx[ %lu ] =\n{\n", uxNextPacketNumber, uxLength );

        for( uxOffset = 0u; uxOffset < uxLength; )
        {
            size_t uxCurLength = 0u;
            size_t uxLast = uxOffset + dumpBYTES_PER_ROW;
            BaseType_t xFirst = pdTRUE;

            if( uxLast > uxLength )
            {
                uxLast = uxLength;
            }

            while( uxOffset < uxLast )
            {
                uxCurLength += snprintf( pcString + uxCurLength, sizeof pcString - uxCurLength, "%s0x%02x",
                                         ( uxCurLength == 0 ) ? "\t" : ", ", pucBuffer[ uxOffset ] );
                uxOffset++;
            }

            if( uxCurLength != 0u )
            {
                uxCurLength += snprintf( pcString + uxCurLength, sizeof pcString - uxCurLength, "%s\n",
                                         ( uxOffset == uxLength ) ? "\n};" : "," );
                fwrite( pcString, 1u, uxCurLength, outfile );
            }
        }

        _fprintf( outfile, "\n" );

        _fprintf( outfile,
                  "DumpPacket_t xPacket_%04lx =\n{\n"
                  "\t.pucData = ucPacket_%04lx,\n"
                  "\t.uxLength = %lu,\n"
                  "\t.ulType = 0x%lX, /* %s */\n",
                  uxNextPacketNumber, uxNextPacketNumber, uxLength, ulTypeMask, pcTypeString );

        if( usSourcePort != 0u )
        {
            _fprintf( outfile,
                      "\t.usSource = %u,\n", FreeRTOS_ntohs( usSourcePort ) );
        }

        if( usSourcePort != 0u )
        {
            _fprintf( outfile,
                      "\t.usDestination = %u,\n", FreeRTOS_ntohs( usDestinationPort ) );
        }

        _fprintf( outfile,
                  "};\n"
                  "/*-----------------------------------------------------------*/\n\n" );
        fclose( outfile );
        uxNextPacketNumber++;
    }
/*-----------------------------------------------------------*/

#endif /* ( ipconfigUSE_DUMP_PACKETS != 0 ) */
